#include "SequenceGraphViz.h"
#include <iostream>
#include <iomanip>
#include <vector>

using namespace std;

SequenceGraphViz::SequenceGraphViz(SequenceGraph* seqGraph, const string& outputDir)
	: 	m_seqGraph(seqGraph), m_OutputDir(outputDir), m_components(0),
	m_totalComponentSize(0), m_largestComponentSize(0)
{
	int arraySize = ((seqGraph->maxVertexID()) >> 3) + 1; // maxid / 8 bits + 1 makes enough space
	m_visited = new unsigned char[arraySize];
	memset(m_visited, 0, arraySize);
}


SequenceGraphViz::~SequenceGraphViz(void)
{
	delete[] m_visited;
}

int SequenceGraphViz::InternalBFS(GID_T curID, ofstream& nodeStream, ofstream& edgeStream)
{
	if (isVisited(curID)) {
		return 0;
	}
	int componentSize = KmerVertex::getLength() - 1; // estimate how many valid bases will be here
	SequenceVertex* vertex = m_seqGraph->getVertexByID(curID);
	//vector<SequenceVertex*> succ = m_seqGraph->getSuccessors(vertex);
	vector<SequenceEdge>::const_iterator it;
	GID_T succID;
	GID_T predID;
	//if (succ.size() == 0)
	//{
	//	edgeStream<<id<<"\t"<<0<<endl;
	//	return;
	//}
	setVisited(curID);
	componentSize += m_seqGraph->getVertexByID(curID)->size() - KmerVertex::getLength() + 1;
	writeVertex(vertex, nodeStream);
	m_Queue.push(curID);
	while(!m_Queue.empty())
	{
		curID = m_Queue.front();
		vertex = m_seqGraph->getVertexByID(curID);
		m_Queue.pop();
		const vector<SequenceEdge>& pred = m_seqGraph->getPredecessors(vertex);
		for (it = pred.begin(); it != pred.end(); ++it)
		{
			predID = it->end->getVertexID();
			if ( !isVisited(abs(predID)) )
			{
				setVisited(abs(predID));
				componentSize += m_seqGraph->getVertexByID(curID)->size() - KmerVertex::getLength() + 1;
				writeVertex(it->end, nodeStream);
				//m_Visited[-succID] = VISITED;
				m_Queue.push(predID);
			}
		}
		const vector<SequenceEdge>& succ = m_seqGraph->getSuccessors(vertex);
		for (it = succ.begin(); it != succ.end(); ++it)
		{
			succID = it->end->getVertexID();
			if ( !isVisited(abs(succID)) )
			{
				setVisited(abs(succID));
				componentSize += m_seqGraph->getVertexByID(curID)->size() - KmerVertex::getLength() + 1;
				writeVertex(it->end, nodeStream);
				//m_Visited[-succID] = VISITED;
				m_Queue.push(succID);
			}
			// output edge
			writeEdge(curID, succID, it->multiplicity, edgeStream);
		}
	} // while(!m_Queue.empty())
	return componentSize;
}

void SequenceGraphViz::writeVertex(SequenceVertex * v, ofstream& nodeStream) {
	nodeStream << v->getVertexID() <<endl;
	nodeStream << v->size() <<endl;
	nodeStream << setprecision(4) << v->coverage() << endl;
	//vector<SequenceVertex*>::const_iterator vit;
	//// predecessor
	//const vector<SequenceVertex*>& preds = m_seqGraph->getPredecessors(v);
	//for (vit = preds.begin(); vit != preds.end(); ++vit)
	//{
	//	nodeStream<<(*vit)->getVertexID()<<" ";
	//}
	//nodeStream<<endl;
	//// successors
	//const vector<SequenceVertex*>& succs = m_seqGraph->getSuccessors(v);
	//for(vit = succs.begin(); vit != succs.end(); ++vit)
	//{
	//	nodeStream<<(*vit)->getVertexID()<<" ";
	//}
	//nodeStream << endl;
	// create data
	v->decompressVertex(*m_seqGraph, shared_buffer);
	nodeStream << shared_buffer << endl;
}

void SequenceGraphViz::writeEdge(GID_T onode, GID_T inode, GINT32 multiplicity, ofstream& edgeStream)
{
	edgeStream << onode << "\t" << inode << endl;
	edgeStream << multiplicity << endl;
}

void SequenceGraphViz::writeData()
{
	char idstr[32];
	m_seqGraph->resetCursor();
	GID_T curID;
	while (m_seqGraph->hasNext())
	{
		curID = m_seqGraph->next()->getVertexID();
		if (!isVisited(curID))
		{
			++m_components;
			snprintf(idstr, sizeof(idstr), "%d", curID);
			string nodeFile = m_OutputDir + idstr + ".nodes";
			ofstream nodeStream(nodeFile.c_str());
			string edgeFile = m_OutputDir + idstr + ".edges";
			ofstream edgeStream(edgeFile.c_str());
			int csize = InternalBFS(curID, nodeStream, edgeStream);
			m_totalComponentSize += csize;
			m_largestComponentSize = max(csize, m_largestComponentSize);
			nodeStream.close();
			edgeStream.close();
		} // if (!isVisited(curID))
	}
}